import _thread
import base64
import json
import pathlib
import pickle
import threading
import time

import cv2
import numpy as np
import pyscreeze
import requests
from entity.Prosumer import CONSUMER
from entity.Project import Project
from service import emService
from entity.Image import Image
from entity.Operation import Click, Drag, Input, Operation


class Log:
    """
    10000: 执行指令
    10001: 本地进程服务器访问失败
    10002: 正常终止
    10003: 异常终止
    11001: 没有找到指令
    11002: 打印日志
    11003: 本地没有找到资源
    """
    def __init__(self, project_path, det, process_ip, process_json, log_id):
        self.root = det
        self.start_time = time.time()
        self.ip = process_ip
        self.process_json = process_json
        self.log_id = log_id

    def info_log(self, message, data=""):
        CONSUMER.push(self.ip, "11002", message, data)

    def send(self, code, message, data=None):
        CONSUMER.push(self.ip, code, message, data)

    def get_process_status(self):
        _res = requests.get(f"http://127.0.0.1:11113/process/{self.process_json['process']}").json()
        if _res["code"] != "200":
            return 0
        return 1

    def end(self):
        """正常退出"""
        self.show()
        self.info_log(f"正常退出，项目耗时："
                      f"{(time.time() - self.start_time)}秒")
        CONSUMER.push(self.ip, "10002", "", "")
        self.root.end = True
        emService.stop_local_process(self.process_json, self.log_id)

    def err(self):
        """异常退出"""
        self.info_log(f"异常退出，项目耗时："
                      f"{(time.time() - self.start_time)}秒")
        CONSUMER.push(self.ip, "10003", "", "")
        self.root.end = True
        emService.stop_local_process(self.process_json, self.log_id)

    @staticmethod
    def hide():
        """隐藏窗口"""
        pass

    def show(self):
        """展示窗口"""
        pass


class Det:
    def __init__(self, project, process_ip, process_json, log_id):
        self.root = None
        self.project = None
        self.process = None
        self.is_ddt = False
        self.id = 0
        self.time = None
        self.thread = None
        self.end = False
        # 场景列表
        self.scene = []
        self.url = f"http://{process_ip}:11114"

        self.root = Log(project, self, process_ip, process_json, log_id)
        self.project = project

        self.project_path = pathlib.Path(self.project)
        self.config_path = self.project / "app.dsft.ini"
        # 载入配置文件
        if not self.load_conf():
            self.root.info_log("载入配置文件检查失败")
            self.root.err()

    def run(self):
        """开始"""
        self.root.info_log("开始运行")
        if not self.check():
            self.root.info_log("项目文件检查失败")
            self.root.err()
            return False
        self.root.info_log("检查运行配置通过")
        # 等待应用程序打开，等待2分钟
        self.root.info_log("查询应用程序是否打开，等待2分钟")
        start_time = time.time()
        # 启动一个进程，一个获取self.project.config.app状态
        _thread.start_new_thread(self.inspect_app, ())
        self.root.send("11004", self.project.config.app_name, self.project.config.resolving_power)
        while 1:
            if time.time() - start_time > (60 * 2):
                self.root.info_log("没有找到应用程序，退出程序")
                self.root.err()
                return False
            if self.project.config.app is True:
                self.root.info_log(f"检测到应用程序已打开，用时：{time.time() - start_time}秒")
                break
            time.sleep(5)
        """创建本地线程"""
        self.thread = threading.Thread(target=self.local_process, args=())
        self.thread.setDaemon(True)
        self.thread.start()

    def local_process(self):
        """本地进程"""
        self.time = time.time()
        for i in self.project.config.run_order:
            if self.end:
                return "end"
            scene_dict = self.a_scene_jpgs_to_list()
            self.root.info_log(f"进入场景{scene_dict['name']}")
            self.while_scene(scene_dict)

    def while_scene(self, scene_dict):
        """循环找图，直到超时或者找到特征跳转"""
        self.root.info_log(f"循环找图，直到超时或者找到特征跳转")
        start_time = time.time()
        screen_err = 0
        while 1:
            time.sleep(0.2)
            if self.end:
                return "end"
            if not self.root.get_process_status():
                self.end = True
                return "end"
            if time.time() - start_time >= float(scene_dict["delay_time"]) * 60:
                self.root.info_log(f"超时({scene_dict['delay_time']}分)退出场景{scene_dict['name']}")
                return True
            try:
                screen = requests.get(f"{self.url}/screenshot").content
                img_buffer_numpy = np.frombuffer(screen, dtype=np.uint8)
                img_numpy = cv2.imdecode(img_buffer_numpy, 1)
                screen = cv2.cvtColor(img_numpy, cv2.COLOR_BGR2GRAY)
                screen_err = 0
            except requests.exceptions.ConnectionError:
                screen_err += 1
                self.root.info_log(f"无法获取设备屏幕截图, {screen_err}次")
                if screen_err > 5:
                    self.root.info_log("无法获取设备屏幕截图，退出")
                    self.root.err()
                    self.end = True
                    return "end"
                continue
            for i in self.scene:
                _scene = scene_dict['name']
                try:
                    result = find_position(screen, i.img_cv, float(i.threshold))
                except TypeError:
                    self.root.info_log(f"捕获到有损坏的图片，位置在第{self.scene.index(i)+1}个，"
                                      f"请用ddt工具查看")
                    self.root.err()
                if result:
                    self.root.info_log(f"{i.scene}/{i.desc}已识别")
                    self.execute(i.operation, result)
                    _next = self.jump_scene(i.desc, scene_dict)
                    if _next:
                        self.root.info_log(f"准备跳转场景{i.desc}->{_next}")
                        scene_dict = self.next(_next)
                    if not scene_dict:
                        self.root.info_log(f"找不到跳转场景{i.desc}->???")
                        self.root.err()
                    if scene_dict == "end":
                        return "end"
                    if scene_dict['name'] == _scene:
                        continue
                    self.root.info_log(f"进入场景{scene_dict['name']}")
                    scene_dict = self.a_scene_jpgs_to_list(scene_dict)
                    self.while_scene(scene_dict)

    def next(self, _next):
        """加载下一个跳转"""
        for scene_dict in self.project.config.run_order:
            if scene_dict["name"] == _next:
                return scene_dict
            if _next == "正常终止":
                self.root.end()
            if _next == "异常终止":
                self.root.err()
        if self.end:
            return "end"
        return False

    @staticmethod
    def jump_scene(name, scene_dict):
        """查询跳转"""
        if name in scene_dict["jump"]:
            _next = scene_dict["jump"][name]
            return _next
        return False

    def execute(self, operation_list, result):
        for i in range(len(operation_list)):
            operation = get_min_id_value(operation_list)
            print(type(operation))
            self.root.send("10000", "", [(operation.__, operation.__dict__), result])

    def a_scene_jpgs_to_list(self, _dict=False):
        """把一个场景中的特征载入场景列表"""
        if _dict:
            scene_dict = _dict
        else:
            scene_dict = self.get_next_scene()
        scene_path = self.project.project / scene_dict["name"]
        self.scene = []
        for i in scene_path.rglob("*.jpg"):
            if i.is_file():
                _image = Image()
                _image.set(i.absolute())
                self.scene.append(_image)
        return scene_dict

    def get_next_scene(self):
        """获取下一个场景列表"""
        self.id += 1
        if self.id > len(self.project.config.run_order):
            self.root.end()
        _res = {}
        for i in self.project.config.run_order:
            if i["id"] == self.id:
                _res = i
        if _res == {}:
            self.root.info_log("设置中场景执行顺序有误！必须从1开始连贯的整数")
            self.root.err()
        return _res

    def inspect_app(self):
        """监视应用程序是否存在的线程"""
        init = False
        while 1:
            if self.end:
                return "end"
            time.sleep(1)
            try:
                resp = requests.get(url=self.url + "/app").text
                if resp == "end":
                    return "end"
            except requests.exceptions.ConnectionError:
                return "end"
            if resp == "True" and init is False:
                self.project.config.app = True
                init = True
            if init is True and resp != "True":
                self.root.info_log("检测不到窗口，进程异常终止")
                return self.root.err()
            if self.end:
                return "end"

    def check(self):
        """检查运行配置"""
        self.root.info_log("项目文件检查")
        if not self.project_path.is_dir():
            self.root.info_log("没有找到项目路径")
            return False
        self.root.info_log("项目路径检查通过")
        if not self.config_path.is_file():
            self.root.info_log("没有找到项目配置文件路径")
            return False
        self.root.info_log("项目配置文件路径检查通过")
        return True

    def load_conf(self):
        """把配置文件载入内存"""
        self.root.info_log("载入配置文件检查")
        try:
            self.project = Project(self.project_path, self.config_path)
            self.project.config.read_config()
        except Exception as e:
            self.root.info_log(f"捕获到异常 {e}")
            return False
        self.root.info_log("载入配置文件检查通过")
        return True


def find_position(screen, template, threshold):
    image_x, image_y = template.shape[:2]
    result = cv2.matchTemplate(screen, template, cv2.TM_CCOEFF_NORMED)  # 找图函数
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
    if max_val > threshold:
        center = (max_loc[0] + image_y / 2, max_loc[1] + image_x / 2)
        return center  # 坐标
    else:
        return False


def get_min_id_value(_list):
    _res = Operation()
    _res.id = 9999
    index = 0
    _index = 0
    if len(_list) == 1:
        return _list[0]
    for i in _list:
        if int(i.id) < int(_res.id):
            _res = i
            index = _index
        _index += 1
    _list.pop(index)
    return _res